package pers.brozen.anka.support.web.response;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import pers.brozen.anka.support.i18n.I18nHelper;

import java.util.Locale;
import java.util.Optional;

/**
 * 处理controller层抛出的异常
 *
 * @author Brozen
 * @since 2021-12-21
 */
@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {

//    @Autowired
//    @Qualifier("errorCodesI18nHelper")
    private I18nHelper i18nHelper;

    /**
     * 所有未处理的异常最终执行分支
     */
    @ExceptionHandler(value = Exception.class)
    public Response<?> handleException(Exception e) {
        log.error("接口处理抛出未知异常", e);
        return response(ErrorCodes.UNKNOWN);
    }


    /**
     * 接口请求method不支持时，返回接口不存在，错误码 500
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Response<?> handleException(HttpRequestMethodNotSupportedException e) {
        log.error("接口不存在", e);
        return new Response<>(ErrorCodes.UNKNOWN.code, e.getMessage());
    }


    /**
     * 缺少必要参数、或参数类型不对时，返回参数错误，错误码 414
     */
    @ExceptionHandler(value = { MissingServletRequestParameterException.class, ServletRequestBindingException.class,
            ConstraintViolationException.class, BindException.class,
            MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class })
    public Response<?> handleParamException(Exception e) {
        return new Response<>(ErrorCodes.PARAM_ERROR.code, e.getMessage());
    }


    /**
     * 参数校验异常时，返回参数错误，错误码 414
     */
    @ExceptionHandler(value = { MethodArgumentNotValidException.class })
    public Response<?> handleBindResultParamException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        if (fieldError != null) {
            return new Response<>(ErrorCodes.PARAM_ERROR.code, fieldError.getField() + " " + fieldError.getDefaultMessage());
        }
        return new Response<>(ErrorCodes.PARAM_ERROR.code, bindingResult.getAllErrors().get(0).toString());
    }


    /**
     * 返回带自定义错误码的ResponseException时，使用指定错误码的Response
     */
    @ExceptionHandler(value = { ResponseException.class })
    public Response<?> handleResponseException(ResponseException e) {
        // 生成Response
        Response<Object> response = new Response<>(e.getData());
        response.setStatus(e.getStatus().code);

        // 设置了错误信息，则覆盖
        if (StringUtils.isNotBlank(e.getMessage())) {
            response.setMessage(e.getMessage());
        } else {
            response.setMessage(errorMsg(e.getStatus(), e.getMsgArgs()));
        }

        return response;
    }


//    /**
//     * 分布式锁锁定异常，返回锁定资源失败，错误码 5201
//     */
//    @ExceptionHandler(value = { LockException.class })
//    public Response<?> handleResponseException(LockException e) {
//        return response(ErrCodeEnum.LOCK_RESOURCE_FAILED);
//    }


    /**
     * 将错误码转换为响应，会填充错误码状态值、国际化后的错误信息
     */
    private <T> Response<T> response(ErrorCodes status) {
        return new Response<>(status.code, errorMsg(status));
    }


    /**
     * 将错误码转换为错误信息，根据RequestContextHolder中的ServletRequest决定Locale，也即根据Accept-Language决定。
     */
    private String errorMsg(ErrorCodes status, Object... args) {
        return errorMsg(status.code, status.message, args);
    }


    /**
     * 将错误码转换为错误信息，根据RequestContextHolder中的ServletRequest决定Locale，也即根据Accept-Language决定。
     */
    private String errorMsg(Integer status, String defaultMessage, Object... args) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Locale locale = Optional.ofNullable(attributes)
                .map(ServletRequestAttributes::getRequest)
                .map(HttpServletRequest::getLocale)
                .orElse(Locale.CHINESE);
        return i18nHelper == null ? defaultMessage : i18nHelper.msgOrDefault("error.msg." + status, defaultMessage, locale, args);
    }

}
